package xfuzzy.alg_genetico.indeterminado.model;

import java.util.Observable;

import xfuzzy.xfghl.model.XfghlConfig;
import xfuzzy.xfhl.model.XfhlData;
import xfuzzy.xfhl.model.XfhlProcess;
import xfuzzy.xfhl.model.hierStruct.XfhlSingleHierStruct;

public class MyXfhlProcess {

	public static final int FINAL_SPEC = 1;
	public static final int UPDATE_BEST = 2;

	/**
	 * Las hebras que van a ejecutar nuestro sistema
	 */
	private MyXfhlThread[] xfhlThread;

	/**
	 * Objetos XfhlData que tienen las hebras
	 */
	private XfhlData[] xfhlData;

	/**
	 * El mejor XfhlData hasta el momento
	 */
	private XfhlData xfhlBestData;

	/**
	 * Configuracion de la herramienta Xfhl
	 */
	private XfghlConfig xfhlConf;

	/**
	 * Contador de estructuras
	 */
	private int contStruc = 0;

	/**
	 * Variable que utilizaremos para acumular todo el tiempo que llevamos
	 */
	private long timeTotal = 0;

	/**
	 * Para informar del cambio en el proceso
	 */
	private int CHANGE = -1;

	/**
	 * Constructor
	 * 
	 * @param conf
	 *            la configuracion de la herramienta
	 */
	public MyXfhlProcess(XfghlConfig conf) {

		this.xfhlConf = conf;
		this.xfhlData = new XfhlData[this.xfhlConf.getNumThread()];

		this.xfhlBestData = new XfhlData(-1, -1);
		this.xfhlBestData.setFinalErrorNotify(-1);

		// Inicializamos nuestro generador de estructuras para las hebras
		XfhlSingleHierStruct.setNumVarEnt(this.xfhlConf.getnumVarEnt());

		// XfhlSingleHierStruct str=XfhlSingleHierStruct.getInstance();
		// str.getXfhlHierStruct();

		for (int i = 0; i < this.xfhlData.length; i++) {
			this.xfhlData[i] = new XfhlData(i, this.xfhlConf.getnumIterAjust());
			// Observamos los Data
			//this.xfhlData[i].addObserver(this);
		}

		// Creamos las hebras
		this.xfhlThread = new MyXfhlThread[this.xfhlConf.getNumThread()];
		for (int i = 0; i < this.xfhlConf.getNumThread(); i++) {
			 this.xfhlThread[i] = new MyXfhlThread(i, this.xfhlConf, this.xfhlData[i]);
		}
	}

	/**
	 * Iniciamos todas las hebras
	 */
	public void ejecucion() {
		
		//for (int i = 0; i < this.xfhlConf.getNumThread(); i++) {
		//	 this.xfhlThread[i].start();
		//}
		this.xfhlThread[0].ejecucion();
	}

	/**
	 * Iniciamos todas las hebras y esperamos a que terminen todas
	 */
	public void runBackground() {
//		for (int i = 0; i < this.xfhlConf.getNumThread(); i++) {
//			this.xfhlThread[i].start();
//		}
//		for (int i = 0; i < this.xfhlConf.getNumThread(); i++) {
//			try {
//				this.xfhlThread[i].join();
//			} 
//			catch (InterruptedException e) {
//				e.printStackTrace();
//			}
//		}
		// System.out.println("XfhlProcess: HE TERMINADOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO DE ESPERAR");
	}

	/**
	 * @return Devuelve el array de XfhlData
	 */
	public XfhlData[] getData() {
		return this.xfhlData;
	}

	/**
	 * 
	 * @return Devuelve la configuracion de la herramienta Xfhl
	 */
	public XfghlConfig getConfig() {
		return this.xfhlConf;
	}

	/**
	 * Metodo para implementar la interfaz Observer Controla cuando termina una
	 * hebra de ajustar y entrenar una estructura para comprobar si es la mejor
	 */
	public void update(Observable o, Object arg) {

		updateSin(o);
	}

	/**
	 * Para tener el update sincronizado
	 * 
	 * @param o
	 *            Observador que ha llamado al update
	 */
	private synchronized void updateSin(Observable o) {

		// Que sea un XfhlData quien ha avisado
		if (o instanceof XfhlData) {
			// Comprobamos el cambio
			int change = ((XfhlData) o).getChange();
			switch (change) {
			case XfhlData.FINAL_SPEC:
				// System.out.println("XfhlProcess: HE ENTRADO EN UPDATE: "+((XfhlData)o).getIdData());
				finishSpec((XfhlData) o);
				break;
			default:
				break;
			}
		}
	}

	/**
	 * Metodo que comprueba si la estructura que acaba de terminar es mejor que
	 * la actual
	 * 
	 * @param data
	 *            XfhlData que ha terminado
	 */
	private synchronized void finishSpec(XfhlData data) {
		
		System.out.println("XfhlProcess: Data " + data.getIdData() + ". Error "
				+ data.getFinalError() + ".");

		// Incrementamos la estructura
		this.contStruc++;

		// Actualizamos el tiempo
		this.timeTotal += data.getTime();

		// informamos de que hemos terminado con una estructura
		this.CHANGE = XfhlProcess.FINAL_SPEC;

		// Si es -1 es la primera specificaci�n calculada
		if (this.xfhlBestData.getFinalError() == -1
				|| data.getFinalError() < this.xfhlBestData.getFinalError()) {
			// Actualizamos el mejor
			this.xfhlBestData.setFinalErrorNotify(data.getFinalError());
			this.xfhlBestData.setSpec(data.getSpec());
			this.CHANGE = XfhlProcess.UPDATE_BEST;

		}
		// System.out.println("XfhlProccess: La especificiacion es:"+data.getSpec().toXfl());
		// Notificamos los cambios
		//setChanged();
		//notifyObservers();
		//clearChanged();

		// System.out.println("XfhlProcess: Tiempo estimado: "+stimateTime());
	}

	/**
	 * 
	 * @return Obtiene la mejor XfhlData hasta el momento
	 */
	public XfhlData getBestData() {
		return this.xfhlBestData;
	}

	/**
	 * Para saber el cambio que ha tenido el objeto
	 * 
	 * @return cambio FINAL_SPEC � UPDATE_BEST
	 */
	public int getChange() {
		return this.CHANGE;
	}

	/**
	 * Funcion que nos estima el tiempo restante
	 * 
	 * @return devuelve un string en formato HH:MM:SS con el tiempo
	 */
	public String stimateTime() {
		if (this.contStruc != 0) {

			int restantes = this.xfhlConf.getNumStruc() - this.contStruc;

			long milisecond = ((this.timeTotal / this.contStruc) * restantes)
					/ this.xfhlData.length;

			// obtenemos los segundos
			long segundos = milisecond / 1000;

			// obtenemos las horas
			long horas = segundos / 3600;

			// restamos las horas para continuar con minutos
			segundos -= horas * 3600;

			// igual que el paso anterior
			long minutos = segundos / 60;
			segundos -= minutos * 60;

			String seg = "" + segundos;
			if (seg.length() == 1)
				seg = "0" + seg;

			String min = "" + minutos;
			if (min.length() == 1)
				min = "0" + min;

			return "" + horas + ":" + min + ":" + seg;
		} else
			return "";
	}

	/**
	 * Para todas las hebras. No podran ser continuadas
	 */

	//@SuppressWarnings("deprecation")
	public void stop() {
//		for (int i = 0; i < xfhlThread.length; i++) {
//			xfhlThread[i].stop();
//		}
	}

	/**
	 * Pausa todas las hebras.
	 */
	//@SuppressWarnings("deprecation")
	public void pause() {
//		for (int i = 0; i < xfhlThread.length; i++) {
//			xfhlThread[i].suspend();
//
//		}
	}

	/**
	 * Continua las hebras que previamente han sido pausadas
	 */
	//@SuppressWarnings("deprecation")
	public void contin() {
//		for (int i = 0; i < xfhlThread.length; i++) {
//			xfhlThread[i].resume();
//		}
	}

}

